﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using DiscoveryLogic.Analytics.BusinessLogic;
using DiscoveryLogic.Analytics.BusinessLogic.Measures;
using DiscoveryLogic.Analytics.BusinessLogic.Rules;
using DiscoveryLogic.Analytics.DataObjects;
using DiscoveryLogic.Analytics.DataObjects.Filters;
using DiscoveryLogic.Analytics.DataObjects.Measures;
using DiscoveryLogic.Analytics.DataObjects.Networks;
using DiscoveryLogic.Analytics.DataObjects.Rules;
using DiscoveryLogic.Analytics.DataObjects.Visuals;
using DiscoveryLogic.Analytics.Diagrams.Physics.Renderers;
using DiscoveryLogic.Analytics.Modules;
using DiscoveryLogic.Analytics.Modules.Explorers;
using DiscoveryLogic.Analytics.Modules.LinkRules;
using DiscoveryLogic.Analytics.Modules.Nodes;
using DiscoveryLogic.Analytics.UI.Common.Events;
using DiscoveryLogic.Analytics.UI.Common.Wizard;
using DiscoveryLogic.Common.Utilities;

namespace DiscoveryLogic.Analytics.Diagrams.Physics
{
	/// <summary>
	/// 
	/// </summary>
	public class FreeParticleCanvas:Canvas
    {
        #region events
	    public delegate void OnVerticeSelected(Vertice v);

	    public event OnVerticeSelected VerticeSelected;
        #endregion

        #region fields
        private readonly DispatcherTimer _WorldTimer = new DispatcherTimer();
		private Point _OwnerWindowPosition;
		private Point _PrevAbsoluteMousePosition;
		private List<Vertice> _SelectedVertices = null;
		private int _ZIndex = 0;
		private int _Iteration = 0;
		#endregion

		#region props
        //private string _NetworkName;
        ///// <summary></summary>
        //public string NetworkName
        //{
        //    get
        //    {
        //        return this._NetworkName;
        //    }
        //    set
        //    {
        //        this._NetworkName = value;
        //    }
        //}

        //private NetworkVisual _NetVisual = NetworkVisual.Default;
        ///// <summary></summary>
        //public NetworkVisual NetVisual
        //{
        //    get
        //    {
        //        return this._NetVisual;
        //    }
        //    set
        //    {
        //        this._NetVisual = value;
        //    }
        //}

        //private NetworkExclusionSetting _ExSetting = NetworkExclusionSetting.Default;
        ///// <summary></summary>
        //public NetworkExclusionSetting ExSetting
        //{
        //    get
        //    {
        //        return this._ExSetting;
        //    }
        //    set
        //    {
        //        this._ExSetting = value;
        //    }
        //}

        //private NetworkInclusionSetting _InSetting = NetworkInclusionSetting.Default;
        ///// <summary></summary>
        //public NetworkInclusionSetting InSetting
        //{
        //    get
        //    {
        //        return this._InSetting;
        //    }
        //    set
        //    {
        //        this._InSetting = value;
        //    }
        //}

        //private List<Vertice> _Vertices;
        ///// <summary></summary>
        //public List<Vertice> Vertices
        //{
        //    get
        //    {
        //        return this._Vertices;
        //    }
        //    set
        //    {
        //        this._Vertices = value;
        //    }
        //}

        //private List<Edge> _Edges;
        ///// <summary></summary>
        //public List<Edge> Edges
        //{
        //    get
        //    {
        //        return this._Edges;
        //    }
        //    set
        //    {
        //        this._Edges = value;
        //    }
        //}

        private Graph _Graph;
        /// <summary></summary>
        public Graph Graph
        {
            get
            {
                return this._Graph;
            }
            set
            {
                this._Graph = value;
            }
        }

        public string NetworkName
        {
            get
            {
                if (this.Graph != null && this.Graph.Network != null)
                {
                    return this.Graph.Network.NetworkName;
                }
                return null;
            }
        }

		private double _AttractForce=2.0;
		/// <summary></summary>
		public double AttractForce
		{
			get
			{
				return this._AttractForce;
			}
			set
			{
				this._AttractForce = value;
			}
		}

		private double _RepelForce=20.0;
		/// <summary></summary>
		public double RepelForce
		{
			get
			{
				return this._RepelForce;
			}
			set
			{
				this._RepelForce = value;
			}
		}

		private IGraphRenderer _Renderer;
		/// <summary></summary>
		public IGraphRenderer Renderer
		{
			get
			{
				return this._Renderer;
			}
			set
			{
				this._Renderer = value;
			}
		}

		private IGraphMeasurer _Measurer;
		/// <summary></summary>
		public IGraphMeasurer Measurer
		{
			get
			{
				return this._Measurer;
			}
			set
			{
				this._Measurer = value;
			}
		}

		private Window _OwnerWindow;
		/// <summary></summary>
		public Window OwnerWindow
		{
			get
			{
				return this._OwnerWindow;
			}
			set
			{
				if (value != null)
				{
					this._OwnerWindow = value;
					this._OwnerWindowPosition = new Point(0, 0);
					this._OwnerWindow.LocationChanged += new EventHandler(_OwnerWindow_LocationChanged);
				}
			}
		}

		void _OwnerWindow_LocationChanged(object sender, EventArgs e)
		{
			Vector deltaMovement = new Vector(this._OwnerWindowPosition.X - this._OwnerWindow.Left,
				this._OwnerWindowPosition.Y - this._OwnerWindow.Top);
			this._OwnerWindowPosition = new Point(this._OwnerWindow.Left, this._OwnerWindow.Top);
			foreach (Vertice v in this._Graph.Vertices.Values)
			{
				v.Position += deltaMovement;
			}
		}

	    public List<Vertice> SelectedVertices
	    {
	        get
	        {
	            return this._SelectedVertices;
	        }
	    }
		#endregion

        #region scale
        private double scale = 1.0;
        public double Scale
        {
            get { return this.scale; }
            set
            {
                if (this.scale != value)
                {
                    scale = value;
                    this.LayoutTransform = new ScaleTransform(scale, scale);
                }
            }
        }
        #endregion

        #region ctor
        public FreeParticleCanvas()
        {
            //this._Vertices=new List<Vertice>();
            //this._Edges=new List<Edge>();
            this._Graph = null;
            this._WorldTimer.Interval = new TimeSpan(50);
            this._WorldTimer.Tick += new EventHandler(_WorldTimer_Tick);
        }

        public void InitGraph(Network network,
            NetworkVisual networkVisual,
            NetworkInclusionSetting inSettings,
            NetworkExclusionSetting exSettings)
        {
            this._Graph = new Graph(network, networkVisual, inSettings, exSettings);
        }

        //public FreeParticleCanvas(List<Vertice> vertices, List<Edge> edges)
        //    : this()
        //{
        //    this._Vertices = vertices;
        //    this._Edges = edges;
        //}

        //public FreeParticleCanvas(
        //    Dictionary<Guid, Vertice> vertices, Dictionary<Guid, Edge> edges)
        //    : this()
        //{
        //    this._Vertices = new List<Vertice>();
        //    if(vertices !=null && vertices.Count>0)
        //        this._Vertices.AddRange(vertices.Values);
        //    this._Edges = new List<Edge>();
        //    if(edges !=null && edges.Count>0)
        //        this._Edges.AddRange(edges.Values);
        //}
		#endregion

        #region entity controls
        private void UpdateEntityControls()
        {
            this.Children.Clear();
            List<VerticeMeasure> verticeMeasures = NetworkMeasureManager.GetVerticeMeasures(this.NetworkName);
            Dictionary<EntityType, Dictionary<string, VerticeMeasure>> measures =
                NetworkMeasureManager.ToDictionary(verticeMeasures);
            VerticeMeasureRange range = NetworkMeasureManager.GetMeasureRanges(verticeMeasures);
            int w = (int)this.ActualWidth;
            int h = (int) this.ActualHeight;
            Random randGen=new Random();
            foreach (Vertice v in this.Graph.Vertices.Values)
            {
                EntityBase entity = v.Tag;
                VerticeMeasure vM=new VerticeMeasure();
                if (measures.ContainsKey(entity.EntityType) &&
                    measures[entity.EntityType].ContainsKey(entity.PKValue()))
                    vM = measures[entity.EntityType][entity.PKValue()];
                v.Control = this.CreateEntityControl(
                    entity, this.Graph.NetVisual,
                    vM, range);
                v.Position = new Vector(randGen.Next(w), randGen.Next(h));
            }
        }

        private EntityControlBase CreateEntityControl(
            EntityBase entity, NetworkVisual visual,
            VerticeMeasure measure, VerticeMeasureRange range)
        {
            EntityControlBase entityView = null;
            switch (entity.EntityType)
            {
                case EntityType.Persons:
                    entityView = new PersonNode(entity,
                        visual.EntityVisuals[EntityType.Persons],
                        false, measure, range);
                    ((PersonNode)entityView).ExploreSelectedEntity += new OnExploreSelectedEntity(HandleExploreSelectedEntity);
                    ((PersonNode)entityView).DefineNewRelation +=
                        new OnDefineNewRelationRuleFromNetworkEntity(HandleRuleDefinitionFromEntity);
                    break;
                case EntityType.Publications:
                    entityView = new PubNode(entity,
                        visual.EntityVisuals[EntityType.Publications],
                        false, measure, range);
                    ((PubNode)entityView).ExploreSelectedEntity += new OnExploreSelectedEntity(HandleExploreSelectedEntity);
                    ((PubNode)entityView).DefineNewRelation +=
                        new OnDefineNewRelationRuleFromNetworkEntity(HandleRuleDefinitionFromEntity);
                    break;
                case EntityType.Grants:
                    entityView = new GrantNode(entity,
                        visual.EntityVisuals[EntityType.Grants], false, measure, range);
                    ((GrantNode)entityView).ExploreSelectedEntity += new OnExploreSelectedEntity(HandleExploreSelectedEntity);
                    ((GrantNode)entityView).DefineNewRelation +=
                        new OnDefineNewRelationRuleFromNetworkEntity(HandleRuleDefinitionFromEntity);
                    break;
                case EntityType.Organizations:
                    entityView = new OrgNode(entity,
                        visual.EntityVisuals[EntityType.Organizations],
                        false, measure, range);
                    ((OrgNode)entityView).ExploreSelectedEntity += new OnExploreSelectedEntity(HandleExploreSelectedEntity);
                    ((OrgNode)entityView).DefineNewRelation +=
                        new OnDefineNewRelationRuleFromNetworkEntity(HandleRuleDefinitionFromEntity);
                    break;
                case EntityType.Document:
                    entityView =
                        new DocumentNode(entity,
                                         visual.EntityVisuals[EntityType.Document],
                                         false, measure, range);
                    ((DocumentNode)entityView).ExploreSelectedEntity +=new OnExploreSelectedEntity(HandleExploreSelectedEntity);
                    ((DocumentNode) entityView).DefineNewRelation +=
                        new OnDefineNewRelationRuleFromNetworkEntity(HandleRuleDefinitionFromEntity);
                    break;
                case EntityType.Subject:
                    entityView =
                        new SubjectNode(entity,
                                         visual.EntityVisuals[EntityType.Document],
                                         false, measure, range);
                    ((SubjectNode)entityView).ExploreSelectedEntity += new OnExploreSelectedEntity(HandleExploreSelectedEntity);
                    ((SubjectNode)entityView).DefineNewRelation +=
                        new OnDefineNewRelationRuleFromNetworkEntity(HandleRuleDefinitionFromEntity);
                    break;
            }
            entityView.DataContext = entity;
            this.Children.Add(entityView);

            entityView.MouseEnter += new MouseEventHandler(this.Canvas_MouseEnter);
            entityView.MouseDown += new MouseButtonEventHandler(this.Canvas_MouseDown);
            entityView.MouseMove += new MouseEventHandler(this.Canvas_MouseMove);
            entityView.MouseUp += new MouseButtonEventHandler(this.Canvas_MouseUp);

            return entityView;
        }

        private void HandleExploreSelectedEntity(ExploreEntityEventArgs args)
        {
            if (!string.IsNullOrEmpty(this.NetworkName))
            {
                Network network = Network.Read(this.NetworkName);
                ExploreSelectedEntity explorer = new ExploreSelectedEntity(network, args.SrcEntity, args.TgtEntityType);
                PopupWindow dialog = new PopupWindow();
                dialog.Content = explorer;
                dialog.Show();
            }
        }

        private void HandleRuleDefinitionFromEntity(NewRuleFromNetworkEntityArgs args)
        {
            List<IWizardPageBase> wizPages = new List<IWizardPageBase>();
            LinkRuleNetworkSelectedEntity page1 = new LinkRuleNetworkSelectedEntity();
            LinkRuleSqlParameters page2 = new LinkRuleSqlParameters();
            LinkRuleTgtEntityFieldMappings page3 = new LinkRuleTgtEntityFieldMappings();
            LinkRuleDistanceCalculator page4 = new LinkRuleDistanceCalculator();

            page1.PrevPage = null;
            page1.NextPage = page2;

            page2.PrevPage = page1;
            page2.NextPage = page3;

            page3.PrevPage = page2;
            page3.NextPage = page4;

            page4.PrevPage = page3;
            page4.NextPage = null;

            wizPages.Add(page1);
            wizPages.Add(page2);
            wizPages.Add(page3);
            wizPages.Add(page4);
            EntityRelationRule rule =
                new EntityRelationRule(
                    "Link Rule", "/images/rule.png",
                    this.NetworkName,
                    args.Entity.EntityType,
                    EntityType.Grants,
                    args.Entity.PKValue());
            rule.SrcEntityType = args.Entity.EntityType;
            Dictionary<string, object> sharedVars = new Dictionary<string, object>();
            sharedVars.Add("LinkRule", rule);
            Wizard wiz = new Wizard(sharedVars, wizPages.ToArray(), page1);
            wiz.Finish += new Wizard.OnFinish(wizLinkRule_Finish);
            wiz.WindowStartupLocation = WindowStartupLocation.CenterScreen;
            wiz.Show();
        }

        private void wizLinkRule_Finish(Dictionary<string, object> sharedVars)
        {
            EntityRelationRule rule = (EntityRelationRule)sharedVars["LinkRule"];
            RuleManager.Save(rule);
            MyData.ReadAllRules();
        }

        #endregion

        #region layout
        protected override Size ArrangeOverride(Size arrangeSize)
		{
			foreach (UIElement element in this.InternalChildren)
			{
				double left = Canvas.GetLeft(element);
				double top = Canvas.GetTop(element);
				double x = double.IsNaN(left) ? 0 : left;
				double y = double.IsNaN(top) ? 0 : top;
				element.Arrange(new Rect(new Point(x, y), element.DesiredSize));
			}
			return arrangeSize;
		}

		protected override Size MeasureOverride(Size constraint)
		{
			Size size = new Size(double.PositiveInfinity, double.PositiveInfinity);
			foreach (UIElement element in this.InternalChildren)
			{
				element.Measure(size);
			}
			return new Size();
		}

		protected override void OnRender(System.Windows.Media.DrawingContext dc)
		{
			base.OnRender(dc);
			if (this.Renderer != null)
				this.Renderer.RenderEdges(dc, 
                    this._Graph.NetVisual,
                    this._Graph.Vertices.Values.ToList(), 
                    this._Graph.Edges.Values.ToList());
		}
		#endregion

		#region simulation
		public void StartSimulation()
		{
            if(this._WorldTimer.IsEnabled)
                this.StopSimulation();

			if(!this._WorldTimer.IsEnabled)
			{
                this.UpdateEntityControls();
				this._Iteration = 0;
			    this._Renderer = new ForceDirected();
                this._Renderer.GraphRenderFinished += new OnFinishedGraphRendering(_Renderer_GraphRenderFinished);
				this._WorldTimer.IsEnabled = true;
			}
		}

        void _Renderer_GraphRenderFinished()
        {
            this.StopSimulation();
        }

		public void StopSimulation()
		{
            if(_WorldTimer.IsEnabled)
                _WorldTimer.Stop();
			this._WorldTimer.IsEnabled = false;
			this._Iteration = 0;
		}

		void _WorldTimer_Tick(object sender, EventArgs e)
		{
			Rect constraintRect = new Rect(0, 0, this.ActualWidth, this.ActualHeight);
			lock (this._Graph)
			{
				this._Iteration++;
                
				if(this._Renderer !=null)
				{
                    if (this._Renderer.IsStopped)
                    {
                        this.StopSimulation();
                    }
                    else
                    {
                        this._Renderer.UpdatePositions(
                            this._Graph.Vertices.Values.ToList(), 
                            this._Graph.Edges.Values.ToList(),
                            constraintRect, this._Iteration,
                            this._AttractForce, this._RepelForce);
                    }
				}
				foreach (Vertice v in this._Graph.Vertices.Values)
				{
                    v.SetDisabled(this.IsDisabled(v, 
                        this._Graph.InSetting, 
                        this._Graph.ExSetting), 
                        this._Graph.NetVisual.EntityVisuals[v.Tag.EntityType]);
					v.SnapControl();
				}
			}
			this.InvalidateVisual();
		}
		#endregion

		#region drag-drop
		public void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
		{
			if (e.ButtonState == MouseButtonState.Released)
			{
                foreach(Vertice v in this._Graph.Vertices.Values)
                    v.CanMove = true;
			}
		}

		public void Canvas_MouseMove(object sender, MouseEventArgs e)
		{
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                Point absolutePosition = Mouse.GetPosition(this);
                Rect constraintRect = new Rect(0, 0, this.ActualWidth, this.ActualHeight);
                foreach (Vertice v in this._Graph.Vertices.Values)
                {
                    if (v.Selected)
                    {
                        v.SetPosition(
                            new Vector(
                                v.Position.X +
                                (absolutePosition.X - this._PrevAbsoluteMousePosition.X),
                                v.Position.Y +
                                (absolutePosition.Y - this._PrevAbsoluteMousePosition.Y)),
                            constraintRect);
                        v.SnapControl();
                    }
                }
                this._PrevAbsoluteMousePosition = absolutePosition;
                this.InvalidateVisual();
            }
		}

		public void Canvas_MouseDown(object sender, MouseEventArgs e)
		{
			if (e.LeftButton == MouseButtonState.Pressed)
			{
				this._PrevAbsoluteMousePosition = Mouse.GetPosition(this);
				var verticeSelected = from particle in this._Graph.Vertices.Values
									where particle.Control == sender
									select particle;
				if (verticeSelected.Count() > 0)
				{
					Vertice v = verticeSelected.First();
                    foreach(Vertice v2 in this._Graph.Vertices.Values)
                    {
                        if(v2!=v)
                        {
                            v2.SetSelected(false, this._Graph.NetVisual.EntityVisuals[v2.Tag.EntityType]);
                            v2.CanMove = true;
                        }
                    }
				    v.SetSelected(true,this._Graph.NetVisual.EntityVisuals[v.Tag.EntityType]);
				    v.CanMove = false;
					v.Control.SetValue(Canvas.ZIndexProperty, this._ZIndex++);
				    this._SelectedVertices = new List<Vertice>();
				    this._SelectedVertices.Add(v);
                    
                    if (this.VerticeSelected != null)
                        this.VerticeSelected(v);
					return;
				}
                else
				{
                    this._SelectedVertices = new List<Vertice>();
                    foreach (Vertice v in this._Graph.Vertices.Values)
                        v.SetSelected(false, this._Graph.NetVisual.EntityVisuals[v.Tag.EntityType]);
				}
			}
		}

		public void Canvas_MouseEnter(object sender, MouseEventArgs e)
		{
			(sender as Control).SetValue(Canvas.ZIndexProperty, this._ZIndex++);
		}
		#endregion

        #region selection 
	    public void SelectEntity(EntityBase entity)
	    {
            this._SelectedVertices = new List<Vertice>();
	        foreach(Vertice v2 in this._Graph.Vertices.Values)
	        {
	            if(v2.Tag.PKValue()==entity.PKValue())
	            {
	                v2.SetSelected(true, this._Graph.NetVisual.EntityVisuals[v2.Tag.EntityType]);
                    this._SelectedVertices.Add(v2);
	            }
                else
	            {
	                v2.SetSelected(false, this._Graph.NetVisual.EntityVisuals[v2.Tag.EntityType]);
	            }
	        }
            this.InvalidateVisual();
	    }
        #endregion

        #region filter 
        private bool IsDisabled(Vertice v, NetworkInclusionSetting inSetting, NetworkExclusionSetting exSetting)
        {
            if(!inSetting.IsEnabled && !exSetting.IsEnabled)
                return false;
            if(inSetting.IsEnabled)
            {
                if(inSetting.IncludeEntityTypes.Contains(v.Tag.EntityType))
                    return false;
            }
            if(exSetting.IsEnabled)
            {
                if(exSetting.ExcludeEntityTypes.Contains(v.Tag.EntityType))
                    return true;
            }
            return false;
        }
        #endregion
    }
}
